package com.phychips.rcp;


import android.annotation.SuppressLint;

/**
 * RcpLib defines the internal library functions for RCP.
 *
 */
public class RcpLib {
	static private int[] crc16tab= {
		0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,
		0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,
		0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,
		0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,
		0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,
		0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,
		0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,
		0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,
		0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,
		0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,
		0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,
		0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,
		0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,
		0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,
		0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,
		0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,
		0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,
		0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,
		0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,
		0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,
		0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,
		0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,
		0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,
		0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,
		0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,
		0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,
		0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,
		0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,
		0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,
		0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,
		0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,
		0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0
	};
	
	/**
	 * This calculates CRC16 from the RCP packet
	 * @param rcpPacket : packet
	 * @return CRC16
	 */
	public static int calculateCRC(RcpPacket rcpPacket)
	{
		int i;
		int crc = 0xFFFF;
		
		for (i = 1; i <= (RcpPacket.PAYLOAD_INDEX + rcpPacket.payload_len); i++)
		{
		    crc = ((crc<<8) ^ crc16tab[((crc>>8) ^ rcpPacket.packet[i])&0x00FF]) & 0xFFFF;
		}
        
		return crc;
	}


//	/**
//	 * This calculates CRC16 from the RCP packet
//	 * @param target : packet
//	 * @return CRC16
//	 */
//	public static int calculateCRC(byte[] target)
//	{
//		int i;
//		int crc = 0xFFFF;
//		
//		for (i = 1; i < target.length; i++)
//		{
//		    crc = ((crc<<8) ^ crc16tab[((crc>>8) ^ target[i])&0x00FF]) & 0xFFFF;
//		}
//
//		/*
//		String tmpCrc = String.format("%04x", crc);
//		
//		return RcpLib.convertStringToByteArray(tmpCrc);
//		*/
//        
//		return crc;
//	}
//	// << 20141010, HYO, for test


	
	public static int calculateCRC(byte[] rcpPacket)
	{	    
		int i;
		int crc = 0xFFFF;
		//byte[] ba = new byte[rcpPacket.length + 1];
		
		//for (i = 1; i <= (RcpPacket.PAYLOAD_INDEX + rcpPacket.length); i++)
		for (i = 1; i < rcpPacket.length; i++)
		{
		    int index = ((crc>>8) ^ rcpPacket[i])&0x00FF;
		    
		    crc = ((crc<<8) ^ crc16tab[index]) & 0xFFFF;
		}
        
		return crc;
	}
	

	public static boolean Chk_CRC(int[] dest)
	{
	    int len = dest.length;
	    int CRC = ((dest[len - 2] << 8) & 0xFF00) + (dest[len - 1] & 0xFF);
	    
	    int CRCChk = Cal_CRC16(dest, len - 2);

	    return (CRC == CRCChk);
	}

	public static int Cal_CRC16(int[] byteBuffer, int length)
	{
	    int CRC = 0xFFFF;
	    for (int i = 1; i < length; i++)
	    {
	    	CRC = ((CRC << 8) ^ crc16tab[((CRC >> 8) ^ byteBuffer[i]) & 0x00ff]);
	    }

	    return CRC & 0xFFFF;
	}

	
	/**
	 * This converts bytes array to string in decimal format.
	 * @param array : byte array
	 * @param start : starting index
	 * @param count : count to convert
	 * @return converted string
	 */
	public static String convertByteArrayToDecString(byte[] array, int start, int count)
	{
		String		string;
		String		result = "";
		int			num;
		
		for (int i=start; i<start+count; i++)
		{
			num = RcpLib.covertUbytetoInt(array[i]);
			string = Integer.toString(num);
			
			result += string;
		}

		return result;		
	}	
	
	/**
	 * This converts bytes array to string in hex format.
	 * @param array : byte array
	 * @param start : starting index
	 * @param count : count to convert
	 * @return converted string
	 */
	public static String convertByteArrayToString(byte[] array, int start, int count)
	{
		String		string;
		String		result = "";
		int			num;
		
		for (int i=start; i<start+count; i++)
		{
			num = RcpLib.covertUbytetoInt(array[i]);
			string = Integer.toHexString(num);
			if (num <= 0x0F)
			{
				result += "0" + string;
			}
			else
			{
				result += string;
			}
		}

		return result;		
	}
	
	/**
	 * This converts bytes array to string in hex format. A space is inserted between each byte.
	 * @param array : byte array
	 * @param start : starting index
	 * @param count : count to convert
	 * @return converted string
	 */
	public static String convertByteArrayToSpaceString(byte[] array, int index, int length)
	{
		String		string;
		String		result = "";
		int			num;
		
		for (int i=index; i<index+length; i++)
		{
			num = RcpLib.covertUbytetoInt(array[i]);
			string = Integer.toHexString(num);
			if (num <= 0x0F)
			{
				result += "0" + string;
			}
			else
			{
				result += string;
			}
			
			if (i < index+length-1)
			{
				result += " ";
			}
		}

		return result;		
	}
	
	@SuppressLint("DefaultLocale")
	public static String int2str(int[] data)
	{
	    StringBuffer sb = new StringBuffer();
	   
	    for (int i = 0; i < data.length; i++)
	    {
	    	sb.append(Integer.toHexString((data[i]&0xff) + 0x100).substring(1).toUpperCase());
	    }
	    return sb.toString();
	}

	public static byte[] int2byteArray(int[] data)
	{
	    byte[] array = new byte[data.length];
	   
	    for (int i = 0; i < data.length; i++)
	    {
	    	array[i] = (byte)(data[i] & 0xff);
	    }
	    return array;
	}
	
	@SuppressLint("DefaultLocale")
	public static String byte2string(byte data){
		
		return new String(Integer.toHexString((data&0xff) + 0x100).substring(1).toUpperCase());
	}
	
	
	@SuppressLint("DefaultLocale")
	public static String byte2str(byte[] data)
	{
	    StringBuilder sb = new StringBuilder();
	   
	    for (int i = 0; i < data.length; i++)
	    {
	    	sb.append(Integer.toHexString((data[i]&0xff) + 0x100).substring(1).toUpperCase());
	    }
	    return sb.toString();
	}
	
	public static String reverseByte2str(byte[] data)
	{
	    StringBuilder sb = new StringBuilder();
	   
	    for (int i = data.length-1; 0<i; i--)
	    {
	    	sb.append(Integer.toHexString((data[i]&0xff) + 0x100).substring(1).toUpperCase());
	    }
	    return sb.toString();
	}
	
	/**
	 * This converts hex string to byte array.
	 * @param string : hex string
	 * @return byte array
	 */
	public static byte[] convertStringToByteArray(String string)
	{
		int		length = string.length();
		int		number1, number2;
		byte[]	array = new byte[length/2];
		
		for (int i=0; i<length; i+=2)
		{
			number1 = Integer.parseInt(string.substring(i, i+1), 16);
			number2 = Integer.parseInt(string.substring(i+1, i+2), 16);

			array[i/2] = (byte) (((number1 << 4) + number2) & 0xFF);
		}
		
		return array;		
	}
	
	/**
	 * This converts hex string to byte array. The string has a space between each byte.
	 * @param string : hex string
	 * @return byte array
	 */
	public static byte[] convertSpaceStringToByteArray(String string)
	{
		int		length = string.length();
		int		number1, number2;
		byte[]	array = new byte[(length+1)/3];
		
		for (int i=0; i<length; i+=3)
		{
			number1 = Integer.parseInt(string.substring(i, i+1), 16);
			number2 = Integer.parseInt(string.substring(i+1, i+2), 16);

			array[i/3] = (byte) (((number1 << 4) + number2) & 0xFF);
		}
		
		return array;		
	}
		
	/**
	 * This decode payload (byte array) to ascii string.
	 * @param rcpPacket : response packet
	 * @return string
	 */
	public static String payloadToString(RcpPacket rcpPacket)
	{
		String 	string;
		char[] 	payload = new char [rcpPacket.payload_len];	
		
		for (int i=0; i<rcpPacket.payload_len; i++)
		{
			payload[i] = (char) rcpPacket.packet[RcpPacket.PAYLOAD_INDEX + i];
		}
		
		string = new String(payload);
		return string;
	}
	
	/**
	 * This converts byte to short. The byte value is regarded as unsigned.
	 * @param number : byte value
	 * @return short value
	 */
	public static short covertUbytetoShort(byte	number)
	{
		short	result = number;
		
		if (result < 0)
		{
			result += Byte.MAX_VALUE;
		}
		return result;
	}
	
	/**
	 * This converts byte to int. The byte value is regarded as unsigned.
	 * @param number : byte value
	 * @return int value
	 */
	public static int covertUbytetoInt(byte	number)
	{
		int	result = number;
		
		if (result < 0)
		{
			result += 0x100;
		}
		return result;
	}	
	
	/**
	 * This converts short to int. The short value is regarded as unsigned.
	 * @param number : short value
	 * @return int value
	 */
	public static int covertUShorttoInt(short number)
	{
		int		result = number;
		
		if (result < 0)
		{
			result += 0x10000;
		}
		return result;
	}
	
	/**
	 * This converts short to byte. 
	 * @param number : short value
	 * @return byte value
	 */
	public static byte covertShorttoByte(short	number)
	{
		byte	result;
		result = (byte) (number & 0xFF);
		return result;
	}	

	/**
	 * This extracts partial bits from the integer value.
	 * @param number : integer value
	 * @param index : starting index. The index starts from LSB.
	 * @param count : bit count to extract
	 * @return : extracted value
	 * @throws RcpException
	 */
	public static int getPartialBits(int number, int index, int count) throws RcpException
	{
		int 				bitmask = 1, i, result = number;
		RcpException		exception;	
		
		if ( (count <= 0 || count >= 32) ||
			 (index < 0 || index >= 32) )
		{
			exception = new RcpException("invalid argument", RcpConst.INVALID_ARGUMENT);
			throw exception;		
		}		
		
		for (i=0; i<count-1; i++)
		{
			bitmask <<= 1;	
			bitmask += 1; 
		}
		bitmask <<= index;
		
		result = result & bitmask;
		result >>= index;
			
		return result;
	}
	
	/** 
	 * This converts byte array to long value
	 * @param array : byte array
	 * @param index : starting index
	 * @param count : count to convert
	 * @return converted value
	 * @throws RcpException
	 */
	public static long convertByteArrayToLong(byte[] array, int index, int count) throws RcpException
	{
		int					i;
		long				result = 0;
		RcpException		exception;	
		
		if ( (count <= 0 || count > 8) ||
			 (index < 0) ||
			 (array.length < (index + count)) )
		{
			exception = new RcpException("invalid argument", RcpConst.INVALID_ARGUMENT);
			throw exception;		
		}	
		
		for (i=0; i<count-1; i++)
		{
			result += covertUbytetoInt(array[index + i]);
			result <<= 8;
		}
		result += covertUbytetoInt(array[index + i]);
		
		return result;
	}
	
	/** 
	 * This converts byte array to int value
	 * @param array : byte array
	 * @param index : starting index
	 * @param count : count to convert
	 * @return converted value
	 * @throws RcpException
	 */
	public static int convertByteArrayToInt(byte[] array, int index, int count) throws RcpException
	{
		int					i;
		int					result = 0;
		RcpException		exception;	
		
		if ( (count <= 0 || count > 4) ||
			 (index < 0) ||
			 (array.length < (index + count)) )
		{
			exception = new RcpException("invalid argument", RcpConst.INVALID_ARGUMENT);
			throw exception;		
		}	
		
		for (i=0; i<count-1; i++)
		{
			result += covertUbytetoInt(array[index + i]);
			result <<= 8;
		}
		result += covertUbytetoInt(array[index + i]);
		
		return result;
	}	
	
	/**
	 * This converts long value to byte array.
	 * @param number : long value
	 * @param array : destination byte array
	 * @param index : starting index
	 * @param count : count to convert
	 * @throws RcpException
	 */
	public static void convertLongToByteArray(long number, byte[] array, int index, int count) throws RcpException
	{
		int					i;
		RcpException		exception;	
		
		if ( (count <= 0 || count > 8) ||
			 (number < 0) ||
			 (index < 0) ||
			 (array.length < (index + count)) )
		{
			exception = new RcpException("invalid argument", RcpConst.INVALID_ARGUMENT);
			throw exception;		
		}
		
		for (i=count-1; i>=0; i--)
		{
			array[index + i] = (byte) (number & 0xFF);
			number  = number >> 8;
		}
		
		return;
	}	
	
	/**
	 * This converts int value to byte array.
	 * @param number : int value
	 * @param array : destination byte array
	 * @param index : starting index
	 * @param count : count to convert
	 * @throws RcpException
	 */
	public static void convertIntToByteArray(int number, byte[] array, int index, int count) throws RcpException
	{
		int	i;
		byte 	num;
		
		for (i=count-1; i>=0; i--)
		{
		    //System.out.print(Integer.toHexString(number) + " ");
		    num = (byte) (number & 0xFF);
		    array[index + i] = num;
		    number  = number >> 8;
		}
		
		return;
	}	
	
	/**
	 * This copies byte array.
	 * @param source : byte array
	 * @param source_index : starting index
	 * @param dest : byte array
	 * @param dest_index : starting index
	 * @param count : count to copy
	 * @throws RcpException
	 */
	public static void copyByteArray(byte[] source, int source_index, byte[] dest, int dest_index, int count) throws RcpException
	{
		int					i;
		//RcpException		exception;	
		
		if (count == 0)
		{
			return;
		}
		
//		if ( (count < 0) ||
//			 (source_index < 0) ||
//			 (dest_index < 0) ||
//			 (source.length < (source_index + count)) ||
//			 (dest.length < (dest_index + count)) ) 
//		{
//			exception = new RcpException("invalid argument", RcpConst.INVALID_ARGUMENT);
//			throw exception;		
//		}	
		
		for (i=0; i<count; i++)
		{
			dest[dest_index + i] = source[source_index + i];
		}
		
		return;
	}

}
